package info.batcloud.fanli.core.service.impl;

import info.batcloud.fanli.core.dto.WalletDTO;
import info.batcloud.fanli.core.constants.MessageKeyConstants;
import info.batcloud.fanli.core.entity.Wallet;
import info.batcloud.fanli.core.entity.WalletFlowDetail;
import info.batcloud.fanli.core.enums.WalletFlowDetailType;
import info.batcloud.fanli.core.enums.WalletValueType;
import info.batcloud.fanli.core.repository.WalletFlowDetailRepository;
import info.batcloud.fanli.core.repository.WalletRepository;
import info.batcloud.fanli.core.service.SystemSettingService;
import info.batcloud.fanli.core.service.WalletService;
import info.batcloud.fanli.core.settings.IntegralSetting;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.inject.Inject;
import java.util.Date;

@Service
public class WalletServiceImpl implements WalletService {

    private static final Logger logger = LoggerFactory.getLogger(WalletServiceImpl.class);

    @Inject
    private WalletRepository walletRepository;

    @Inject
    private WalletFlowDetailRepository walletFlowDetailRepository;

    @Inject
    private SystemSettingService systemSettingService;

    @Override
    @Transactional
    public synchronized WalletChangeResult addIntegral(long userId, float integral, WalletFlowDetailType flowDetailType, String[] context) {
        return addValue(userId, integral, WalletValueType.INTEGRAL, flowDetailType, context);
    }

    @Override
    @Transactional
    public synchronized WalletChangeResult addMoney(long userId, float money, WalletFlowDetailType flowDetailType, String[] context) {
        return addValue(userId, money, WalletValueType.MONEY, flowDetailType, context);
    }

    @Override
    public synchronized WalletDTO findByUserId(long userId) {
        Wallet wallet = walletRepository.findByUserId(userId);
        if (wallet == null) {
            wallet = new Wallet();
            wallet.setUserId(userId);
        }
        return toWalletDto(wallet);
    }

    @Override
    public float exchangeMoneyToIntegral(float money) {
        IntegralSetting integralSetting = systemSettingService.findActiveSetting(IntegralSetting.class);

        return money / integralSetting.getEachIntegralExchangeToMoney();
    }

    @Override
    @Transactional
    public synchronized WalletConsumeResult consumeMoney(long userId, float money, WalletFlowDetailType flowDetailType, String... context) {
        WalletConsumeResult result = new WalletConsumeResult();
        WalletDTO wallet = findByUserId(userId);
        if (money <= 0 || wallet.getMoney() < money) {
            result.setSuccess(false);
            result.setCode(MessageKeyConstants.NO_ENOUGH_MONEY);
            return result;
        }
        result.setSuccess(true);
        WalletChangeResult changeResult = this.addMoney(userId, -money, flowDetailType, context);
        result.setWalletChangeResult(changeResult);
        return result;
    }

    @Override
    public synchronized WalletConsumeResult consumeIntegral(long userId, float integral, WalletFlowDetailType flowDetailType, String... context) {
        WalletConsumeResult result = new WalletConsumeResult();
        WalletDTO wallet = findByUserId(userId);
        if (integral <= 0 || wallet.getIntegral() < integral) {
            result.setSuccess(false);
            result.setCode(MessageKeyConstants.NO_ENOUGH_INTEGRAL);
            return result;
        }
        result.setSuccess(true);
        WalletChangeResult changeResult = this.addIntegral(userId, -integral, flowDetailType, context);
        result.setWalletChangeResult(changeResult);
        return result;
    }

    private synchronized WalletChangeResult addValue(long userId, Number value, WalletValueType type, WalletFlowDetailType flowDetailType, String[] context) {
        logger.info(String.format("用户钱包变更：userId:%s,value:%s,type:%s,flowDetailType:%s,context:%s",
                new Object[]{userId + "", value + "", type.name(), flowDetailType.name(), context}));
        Wallet wallet = walletRepository.findByUserId(userId);
        if (wallet == null) {
            wallet = new Wallet();
            wallet.setUserId(userId);
            wallet.setCreateTime(new Date());
        }
        wallet.setUpdateTime(new Date());
        WalletFlowDetail detail = new WalletFlowDetail();
        logger.info(String.format("用户钱包变更前状态：总获取金额%s,金额%s,消费金额%s,总获取积分%s,积分%s,消费积分%s", new Object[]{wallet.getObtainedMoney(),
                wallet.getMoney(), wallet.getConsumedMoney(), wallet.getObtainedIntegral(), wallet.getIntegral(), wallet.getConsumedIntegral()}));
        switch (type) {
            case MONEY:
                detail.setBeforeValue(wallet.getMoney());
                wallet.setMoney(value.floatValue() + wallet.getMoney());
                detail.setAfterValue(wallet.getMoney());
                if (value.floatValue() > 0) {
                    //如果金额大于0，则保存总获取的现金数量
                    wallet.setObtainedMoney(wallet.getObtainedMoney() + value.floatValue());
                }
                logger.info(String.format("增加金额：增加前%s,增加%s,增加后%s", new Object[]{detail.getBeforeValue() + "", value + "", wallet.getMoney() + ""}));
                break;
            case INTEGRAL:
                detail.setBeforeValue(wallet.getIntegral());
                wallet.setIntegral(value.floatValue() + wallet.getIntegral());
                detail.setAfterValue(wallet.getIntegral());
                if (value.floatValue() > 0) {
                    //如果积分大于0，则保存总获取的积分数量
                    wallet.setObtainedIntegral(value.floatValue() + wallet.getObtainedIntegral());
                }
                logger.info(String.format("增加积分：增加前%s,增加%s,增加后%s", new Object[]{detail.getBeforeValue(), value, wallet.getIntegral()}));
                break;
        }
        walletRepository.save(wallet);
        detail.setUserId(userId);
        detail.setCreateTime(new Date());
        detail.setType(flowDetailType);
        detail.setValue(value.floatValue());
        detail.setValueType(type);
        detail.setContext(context == null ? null : String.join(",", context));
        walletFlowDetailRepository.save(detail);
        WalletChangeResult result = new WalletChangeResult();
        result.setWalletDTO(toWalletDto(wallet));
        result.setWalletDetailId(detail.getId());
        logger.info(String.format("用户钱包变更后状态：总获取金额%s,金额%s,消费金额%s,总获取积分%s,积分%s,消费积分%s", new Object[]{wallet.getObtainedMoney() + "",
                wallet.getMoney() + "", wallet.getConsumedMoney() + "", wallet.getObtainedIntegral() + "", wallet.getIntegral() + "", wallet.getConsumedIntegral() + ""}));
        return result;
    }

    private static WalletDTO toWalletDto(Wallet wallet) {
        WalletDTO dto = new WalletDTO();
        BeanUtils.copyProperties(wallet, dto);
        return dto;
    }
}
